package DataBase;

import java.util.ArrayList;
import java.util.Collections;
import java.util.Date;
import java.util.List;
import socof_webcrawler.SOCOF_WebCrawler;

public final class Coleccao extends Thread{

    private static final List<Ficheiro> lista = Collections.synchronizedList(new ArrayList<Ficheiro>());

    // singleton 
    private Coleccao() {
    }
    
    private static final Coleccao INSTANCE = new Coleccao();
    
    public static synchronized Coleccao getInstance(){
           return INSTANCE;
    }

    /**
     * Devolve AL com todos os FX existentes na BD do dominio
     *
     * @param dominio Recebe o dominio para o qual queremos os ficheiros
     * @return Devolve um ArrayList de Ficheiro
     */
    public synchronized List<Ficheiro> getListaTodosFXDominio(String dominio) {

        //ArrayList<Ficheiro> listaCompleta = new ArrayList<Ficheiro>();
        List<Ficheiro> listaCompleta = Collections.synchronizedList(new ArrayList<Ficheiro>());
        synchronized (listaCompleta) {
        DBAccess bd = new DBAccess();

        listaCompleta = bd.getByDomain(dominio);

        synchronized (lista) {
            for (Ficheiro ficheiro : listaCompleta) {
                lista.add(ficheiro);
            }
        }
        notify();
        return listaCompleta;
        }
    }



    /**
     * Inserir um Ficheiro
     *
     * @param fx objecto Ficheiro para adicionar à BD
     * @return devolve o ID do ficheiro e -1 na eventualidade de erro
     */
    public synchronized int writeData(Ficheiro fx) {
        DBAccess bd = new DBAccess();
        int fxId = bd.writeData(fx);
        fx.setId(fxId);
        boolean existe = false;
        synchronized (lista) {
            
            // Se existir, actualiza na lista local
            for (Ficheiro ficheiro : lista) {
                if (ficheiro.getId() == fx.getId()) {
                    existe = true;
                    //                lista=bd.getCompleteByDomain(fx.getDominio());
                    ficheiro.setFicheirosAtributes(fx.getId(), fx.getDominio(), fx.getFicheiro(),
                            fx.getEstado(), fx.getTamanhosite(), fx.getCaminhosite(),
                            fx.getCaminholocal(), fx.getTipo(), fx.getNiveis(),
                            fx.getActualizado());
                    break;
                } else {
                    existe = false;
                }
            }
            if (!existe && fxId != -1) {
                fx.setId(fxId);
                lista.add(fx);
            }
        }
        notifyAll();
        return fxId;
    }

    /**
     * Inserir um Ficheiro com argumentos separados. Devolve -1 na eventualidade
     * de erro. Destina-se mais a utilizacao interna
     *
     *
     * @param id int com ID do ficheiro (se nulo será actualizado com o ID da
     * BD)
     * @param dominio String com dominio (www.exemplo.com)
     * @param ficheiro String com o nome do ficheiro (paginas.css)
     * @param estado String com estado
     * @param tamanhosite long com o tamanho do ficheiro no site
     * @param caminhosite String com o caminho completo do ficheiro no site com
     * o nome do ficheiro
     * @param caminholocal String com o caminho completo do ficheiro local com o
     * nome do ficheiro
     * @param tipo String com o tipo de ficheiro
     * @param niveis int com o nivel do ficheiro
     * @param actualizado variável do tipo Date com a data da ultima
     * actualizacao
     * @return Devolve um int com o ID de Ficheiro
     */
    private synchronized int writeData(Integer id, String dominio, String ficheiro, String estado,
            long tamanhosite, String caminhosite, String caminholocal,
            String tipo, Integer niveis, Date actualizado) {

        Ficheiro fx = new Ficheiro(id, dominio.toLowerCase(), ficheiro,
                estado.toUpperCase(), tamanhosite, caminhosite, caminholocal,
                tipo.toUpperCase(), niveis, actualizado);

        int fxId = writeData(fx);

        boolean existe = false;
        synchronized (lista) {
            // Se existir, actualiza na lista local
            for (Ficheiro ficheiros : lista) {
                if (ficheiros.getId() == fx.getId()) {
                    existe = true;
                    break;
                } else {
                    existe = false;
                }
            }
            if (!existe && fxId != -1) {
                fx.setId(fxId);
                lista.add(fx);
            }
        }
        notify();
        return fxId;
    }

    /**
     * Escrever todo o AL na BD
     *
     * @param fxTodos ArrayList de Ficheiro a ser inserido na BD
     */
    public synchronized void writeDataAll(ArrayList<Ficheiro> fxTodos) {
        List<Ficheiro> listaTemp = Collections.synchronizedList(new ArrayList<Ficheiro>());
        
//        synchronized (listaTemp) {
            //uma vez que a lista nem sempre vem validada,
            //uso o meu metodo para o fazer
            for (Ficheiro ficheiro : fxTodos) {
                writeData(ficheiro);
            }
        notify();
        }
       /**
     * Inserir um ArrayList de Ficheiro na BD
     *
     * @param ArrayList<Ficheiro> fx Recebe um ArrayList de ficheiro
     * @return não devolve nada
     */
    public synchronized void inserirArrayFicheiros(ArrayList<Ficheiro> fx) {
        
            //uma vez que a lista nem sempre vem validada,
            //uso o meu metodo para o fazer
            for (Ficheiro ficheiro : fx) {
                writeData(ficheiro);
            }
        notify();
    }
        


    /**
     * Inserir um Ficheiro apenas
     *
     * @param fx Objecto do tipo Ficheiro
     * @return devolve -1 na eventualidade de erro
     */
    public synchronized int adicionaFx(Ficheiro fx) {
        
        DBAccess bd = new DBAccess();

        synchronized (lista) {
        int fxId = bd.writeData(fx);
        boolean existe = false;

            // Se existir, actualiza na lista local
            for (Ficheiro ficheiro : lista) {
                if (ficheiro.getId() == fx.getId()) {
                    existe = true;
                    //                lista=bd.getCompleteByDomain(fx.getDominio());
                    ficheiro.setFicheirosAtributes(fx.getId(), fx.getDominio(), fx.getFicheiro(),
                            fx.getEstado(), fx.getTamanhosite(), fx.getCaminhosite(),
                            fx.getCaminholocal(), fx.getTipo(), fx.getNiveis(),
                            fx.getActualizado());
                    break;
                } else {
                    existe = false;
                }
            }
            if (!existe && fxId != -1) {
                fx.setId(fxId);
                lista.add(fx);
            }
           return fxId; 
        }
//        notify();
        
    }

    /**
     * Devolve um Objecto Ficheiro conforme o id
     *
     * @param id int com o ID do objecto que queremos receber
     * @return Ficheiro Objecto do tipo Ficheiro devolvido
     */
    // os gets nao precisam de ser synchronized...
    //public synchronized Ficheiro getFicheiro(int id) {
    public Ficheiro getFicheiro(int id) {
        Ficheiro encontrado = null;
//        synchronized (lista) {
            for (Ficheiro ficheiro : lista) {
                if (ficheiro.getId() == id) {
                    encontrado = ficheiro;
                    break;
                }
            }
//        }
//        notify();
        return encontrado;
    }

// A actulizar a local   //////////////////////////////////////////////////
    /**
     * Actualiza o dominio de um Ficheiro recebendo o id
     *
     * @param id int com id do Ficheiro
     * @param dominio String com o novo dominio
     * @return Ficheiro actualizado
     */
    public synchronized Ficheiro setDominio(int id, String dominio) {
        Ficheiro actualizado = null;
//        synchronized (lista) {
            for (Ficheiro ficheiro : lista) {
                if (ficheiro.getId() == id) {
                    ficheiro.setDominio(dominio);
                    writeData(ficheiro);
                    actualizado = ficheiro;
                    break;
                }
            }
//        }
        notify();
        return actualizado;
    }

    /**
     * Actualiza nome de um Ficheiro recebendo o id
     *
     * @param id int com id do Ficheiro
     * @param nome String com novo nome
     * @return Ficheiro actualizado
     */
    public synchronized Ficheiro setFicheiro(int id, String nome) {
        Ficheiro actualizado = null;
//        synchronized (lista) {
            for (Ficheiro ficheiro : lista) {
                if (ficheiro.getId() == id) {
                    ficheiro.setFicheiro(nome);
                    writeData(ficheiro);
                    actualizado = ficheiro;
                    break;
                }
            }
//        }
        notify();
        return actualizado;
    }

    /**
     * Actualiza Estado de um Ficheiro recebendo o id
     *
     * @param id int com id do Ficheiro
     * @param estado String com novo estado
     * @return Ficheiro actualizado
     */
    public  Ficheiro setEstado(int id, String estado) {
        Ficheiro actualizado = null;
 
        synchronized (lista) {
            for (Ficheiro ficheiro : lista) {
                //Adicionado ignora nulls
                if ((ficheiro.getId() == id)) {
                    ficheiro.setEstado(estado);
                    writeData(ficheiro);
                    actualizado = ficheiro;
                    break;
                } 
            }
        }
        return actualizado;
    }

    /**
     * Actualiza Tamanho de um Ficheiro recebendo o id
     *
     * @param id int com id do Ficheiro
     * @param tamanho long com o novo tamanho
     * @return Ficheiro actualizado
     */
    public Ficheiro setTamanhoSite(int id, long tamanho) {
        Ficheiro actualizado = null;

        synchronized (lista) {
            for (Ficheiro ficheiro : lista) {
                if (ficheiro.getId() == id) {
                    ficheiro.setTamanhosite(tamanho);
                    writeData(ficheiro);
                    actualizado = ficheiro;
                    break;
                }
            }
        }
        return actualizado;
    }

    /**
     * Actualiza Caminho no site de um Ficheiro recebendo o id
     *
     * @param id int com id do Ficheiro
     * @param camSite String com o novo caminho
     * @return Ficheiro actualizado
     */
    public  Ficheiro setCaminhoSite(int id, String camSite) {
        Ficheiro actualizado = null;

        synchronized (lista) {
            for (Ficheiro ficheiro : lista) {
                if (ficheiro.getId() == id) {
                    ficheiro.setCaminhosite(camSite);
                    writeData(ficheiro);
                    actualizado = ficheiro;
                    break;
                }
            }
        }
        return actualizado;
    }

    /**
     * Actualiza CaminhoLocal de um Ficheiro recebendo o id
     *
     * @param id int com id do Ficheiro
     * @param camLocal String com novo caminho local
     * @return Ficheiro actualizado
     */
    public  Ficheiro setCaminhoLocal(int id, String camLocal) {
        Ficheiro actualizado = null;

        synchronized (lista) {
            for (Ficheiro ficheiro : lista) {
                if (ficheiro.getId() == id) {
                    ficheiro.setCaminholocal(camLocal);
                    writeData(ficheiro);
                    actualizado = ficheiro;
                    break;
                }
            }
        }
        return actualizado;
    }

    /**
     * Actualiza o Tipo de um Ficheiro recebendo o id
     *
     * @param id int com id do Ficheiro
     * @param tipo String com o novo tipo de ficheiro
     * @return Ficheiro actualizado
     */
    public  Ficheiro setTipo(int id, String tipo) {
        Ficheiro actualizado = null;

        synchronized (lista) {
            for (Ficheiro ficheiro : lista) {
                if (ficheiro.getId() == id) {
                    ficheiro.setTipo(tipo.toUpperCase());
                    writeData(ficheiro);
                    actualizado = ficheiro;
                    break;
                }
            }
        }
        return actualizado;
    }

    /**
     * Actualiza o numero de Nivies de um Ficheiro recebendo o id
     *
     * @param id int com id do Ficheiro
     * @param nivel int com novo nivel
     * @return Ficheiro actualizado
     */
    public  Ficheiro setNiveis(int id, int nivel) {
        Ficheiro actualizado = null;

        synchronized (lista) {
            for (Ficheiro ficheiro : lista) {
                if (ficheiro.getId() == id) {
                    ficheiro.setNiveis(nivel);
                    writeData(ficheiro);
                    actualizado = ficheiro;
                    break;
                }
            }
        }
        return actualizado;
    }

    /**
     * Actualiza Data de um Ficheiro recebendo o id
     *
     * @param id int com id do Ficheiro
     * @param data Date com o nova data
     * @return Ficheiro actualizado
     */
    public  Ficheiro setActualizado(int id, Date data) {
        Ficheiro actualizado = null;

        synchronized (lista) {
            for (Ficheiro ficheiro : lista) {
                if (ficheiro.getId() == id) {
                    ficheiro.setActualizado(data);
                    writeData(ficheiro);
                    actualizado = ficheiro;
                    break;
                }
            }
        }
        return actualizado;
    }
    
    /**
     * Verifica se podemos considerar a lista vazia ou nao, uma vez que
     * se todos os elementos estiverem completos, é o mesmo que nao ter
     * nada para fazer.
     * @return 
     */
    public synchronized boolean listaVazia(){

        synchronized (lista) {
            if (lista.isEmpty()){
                // Nao esta, e a primeira iteracao
                return false;
            } else {
                for (Ficheiro ficheiro : lista) {
                    if ((ficheiro.getNiveis() <= SOCOF_WebCrawler.getLEVEL()) && 
                            (!(ficheiro.getEstado().equals(DataBase.Ficheiro.DB_STATE_COMPLETE))))
                    return false;
                }
            }
        }
            return true;
    }

//////////////////////////////////////////////
//          METODOS VARIOS                  //
//////////////////////////////////////////////
    /**
     * Devolve AL com todos os FX com estado Incompletos do dominio
     *
     * @param dominio String com o dominio que se deseja
     * @return ArrayList<Ficheiro> de Ficheiro Incompleto
     */
    public  ArrayList<Ficheiro> getListaIncompletos(String dominio) {
        ArrayList<Ficheiro> listaFXIncompletos = new ArrayList<Ficheiro>();
        DBAccess bd = new DBAccess();

        listaFXIncompletos = bd.getIncompleteByDomain(dominio);

        return listaFXIncompletos;
    }

    /**
     * Devolve AL com todos os FX com estado Incompletos do dominio
     *
     * @param dominio String com o dominio que se deseja
     * @return ArrayList<Ficheiro> de Ficheiro Incomplete
     */
    public  ArrayList<Ficheiro> getListaIncomplete(String dominio) {
        ArrayList<Ficheiro> listaFXIncompletos = new ArrayList<Ficheiro>();
        DBAccess bd = new DBAccess();

        listaFXIncompletos = bd.getIncompleteByDomain(dominio);

        return listaFXIncompletos;
    }

    /**
     * Devolve AL com todos os FX com estado DOWNLOAD do dominio
     *
     * @param dominio String com o dominio que se deseja
     * @return ArrayList<Ficheiro> de Ficheiro Incompleto
     */
    public  ArrayList<Ficheiro> getListaDownload(String dominio) {
        ArrayList<Ficheiro> listaFXDownload = new ArrayList<Ficheiro>();

        DBAccess bd = new DBAccess();

        listaFXDownload = bd.getDownloadByDomain(dominio);

        return listaFXDownload;
    }

    /**
     * Devolve AL com todos os FX com estado PARSE do dominio
     *
     * @param dominio String com o dominio que se deseja
     * @return ArrayList<Ficheiro> de Ficheiro Incompleto
     */
    public  ArrayList<Ficheiro> getListaParse(String dominio) {
        ArrayList<Ficheiro> listaFXParse = new ArrayList<Ficheiro>();
        DBAccess bd = new DBAccess();

        listaFXParse = bd.getParseByDomain(dominio);

        return listaFXParse;
    }

    /**
     * Devolve AL com todos os FX com estado PENDING do dominio
     *
     * @param dominio String com o dominio que se deseja
     * @return ArrayList<Ficheiro> de Ficheiro PENDING
     */
    public  ArrayList<Ficheiro> getListaPending(String dominio) {
        ArrayList<Ficheiro> listaFXPending = new ArrayList<Ficheiro>();
        DBAccess bd = new DBAccess();

        listaFXPending = bd.getPendingByDomain(dominio);

        return listaFXPending;
    }

    /**
     * Devolve AL com todos os FX com estado DOWNLOADING do dominio
     *
     * @param dominio String com o dominio que se deseja
     * @return ArrayList<Ficheiro> de Ficheiro DOWNLOADING
     */
    public  ArrayList<Ficheiro> getListaDownloading(String dominio) {
        ArrayList<Ficheiro> listaFXDownloading = new ArrayList<Ficheiro>();
        DBAccess bd = new DBAccess();

        listaFXDownloading = bd.getDownloadingByDomain(dominio);

        return listaFXDownloading;
    }

    /**
     * Devolve AL com todos os FX com estado PARSING do dominio
     *
     * @param dominio String com o dominio que se deseja
     * @return ArrayList<Ficheiro> de Ficheiro PARSING
     */
    public  ArrayList<Ficheiro> getListaParsing(String dominio) {
        ArrayList<Ficheiro> listaFXParsing = new ArrayList<Ficheiro>();
        DBAccess bd = new DBAccess();

        listaFXParsing = bd.getParsingByDomain(dominio);

        return listaFXParsing;
    }

    /**
     * Devolve AL com todos os FX com estado COMPLETE do dominio
     *
     * @param dominio String com o dominio que se deseja
     * @return ArrayList<Ficheiro> de Ficheiro COMPLETE
     */
    public  ArrayList<Ficheiro> getListaComplete(String dominio) {
        ArrayList<Ficheiro> listaFXComplete = new ArrayList<Ficheiro>();
        DBAccess bd = new DBAccess();

        listaFXComplete = bd.getCompleteByDomain(dominio);

        return listaFXComplete;
    }

    /**
     * Devolve AL com todos os FX já descarregados do dominio
     *
     * @param dominio String com o dominio que se deseja
     * @return ArrayList<Ficheiro> de Ficheiro Incompleto
     */
    public  ArrayList<Ficheiro> getListaCompletos(String dominio) {
        ArrayList<Ficheiro> listaFXCompletos = new ArrayList<Ficheiro>();

        DBAccess bd = new DBAccess();

        listaFXCompletos = bd.getCompleteByDomain(dominio);

        return listaFXCompletos;
    }

    /**
     * Devolve AL com todos os FX com tipo BINARY do dominio
     *
     * @param dominio String com o dominio que se deseja
     * @return ArrayList<Ficheiro> de Ficheiro Incompleto
     */
    public  ArrayList<Ficheiro> getListaBinary(String dominio) {
        ArrayList<Ficheiro> listaFXDownload = new ArrayList<Ficheiro>();

        DBAccess bd = new DBAccess();

        listaFXDownload = bd.getByTypeBinary(dominio);

        return listaFXDownload;
    }

    /**
     * Devolve AL com todos os FX com tipo HYPERTEXT do dominio
     *
     * @param dominio String com o dominio que se deseja
     * @return ArrayList<Ficheiro> de Ficheiro Incompleto
     */
    public  ArrayList<Ficheiro> getListaHypertext(String dominio) {
        ArrayList<Ficheiro> listaFXDownload = new ArrayList<Ficheiro>();

        DBAccess bd = new DBAccess();

        listaFXDownload = bd.getByTypeHypertext(dominio);

        return listaFXDownload;
    }

    /**
     * Devolve AL com todos os FX com determinado estado do dominio
     *
     * @param dominio String com o dominio que se deseja
     * @param estado String com o estado do qual se deseja os ficheiros
     * @return ArrayList<Ficheiro> de Ficheiro Incompleto
     */
    public  ArrayList getListaDominioPorEstado(String dominio, String estado) {
        ArrayList<Ficheiro> listaFXPorEstado = new ArrayList<Ficheiro>();

        DBAccess bd = new DBAccess();

        listaFXPorEstado = bd.getByDomainStatus(dominio, estado.toUpperCase());

        return listaFXPorEstado;
    }

    /**
     * Informa se já existe aquele Ficheiro na BD
     *
     * @param fx Objecto ficheiro a testar
     * @return True se existe False se nao
     */
    public  boolean sqlExisteId(Ficheiro fx) {
        DBAccess bd = new DBAccess();

        return bd.sqlExisteId(fx);
    }

    /**
     * Informa se já existe Ficheiro por id na BD
     *
     * @param id int com o ID do ficheiro
     * @return True se existe False se nao
     */
    public  boolean sqlExisteId(int id) {
        DBAccess bd = new DBAccess();

        return bd.sqlExisteId(id);
    }

    /**
     * Devolve um unico ficheiro conforme o id
     *
     * @param id int com o ID do ficheiro desejado
     * @return Objecto Ficheiro pedido
     */
    public  Ficheiro getFicheiroBD(int id) {
        DBAccess bd = new DBAccess();

        return bd.getFicheiro(id);
    }

    /**
     * Devolve CSV com todos os ficheiro do AL local
     *
     * @param dominio String com o dominio do qual queremos o CSV
     * @return CSV com lista de todos os fx do dominio
     */
    public  String toString() {
        synchronized (lista) {
            return this.lista.toString();
        }
    }
}
